package org.xiaoyu.boot.stater.shiro;

import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.config.IniFilterChainResolverFactory;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.config.Ini;
import org.apache.shiro.mgt.SecurityManager;

import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.DelegatingFilterProxy;

/**
 * shiro配置方法
 *
 */
@Configuration
@EnableConfigurationProperties(ShiroProperties.class)
@ConditionalOnBean(SecurityManager.class)
public class ShiroAutoConfiguration {

	protected final Log logger = LogFactory.getLog(ShiroAutoConfiguration.class);
	
	@Bean("shiroFilter")
	@ConditionalOnMissingBean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,
			ShiroProperties shiroProperties) {
		ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
		bean.setLoginUrl(shiroProperties.getLoginUrl());
		bean.setSuccessUrl(shiroProperties.getSuccessUrl());
		bean.setUnauthorizedUrl(shiroProperties.getUnauthorizedUrl());
		Map<String, String> filterFilter = shiroIniUrl(shiroProperties.getFilterChainDefinitionResourcePath());
		if(filterFilter != null) {
			logger.info("优先采用shiro Ini配置方案,如果有spring配置文件的shiro拦截链将会被忽略！");
		}else {
			logger.info("采用spring配置文件的shiro拦截链！");
			filterFilter = orderfilterUrl(shiroProperties.getFilterChainDefinitionOrderMap());
		}
		bean.setFilterChainDefinitionMap(filterFilter);
		bean.setFilters(shiroProperties.getFilters());
		bean.setSecurityManager(securityManager);
		return bean;
	}

	/**
	 * 通過ini配置获取地址权限
	 * 
	 * @param shiroProperties
	 * @return
	 */
	private Map<String, String> shiroIniUrl(String url) {
		Ini ini = null;
		Ini.Section section = null;
		try {
			ini = Ini.fromResourcePath(url);
			section = ini.getSection(IniFilterChainResolverFactory.URLS);
			if (CollectionUtils.isEmpty(section)) {
				section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
			}
		}catch (IllegalArgumentException e) {
			if(logger.isDebugEnabled()) {
				logger.debug("FilterChainDefinition Resource Path argument cannot be null or empty.");
			}
		}
		return section;	
	}
	
	/**
	 * 获取排序后的拦截链
	 * @param urls
	 * @return
	 */
	private Map<String, String> orderfilterUrl(TreeMap<Integer, String> urls){
		if(urls == null || urls.isEmpty()) {
			return null;
		}
		Map<String, String> orderMap = new LinkedHashMap<>();
		for (Integer k : urls.keySet()) {
			String url = urls.get(k);
			if(!StringUtils.isEmpty(url)) {
				String[] u = url.split("=");
				if(u.length == 2) {
					orderMap.put(u[0], u[1]);
				}
			}
		}
		return orderMap;
	}
	

	/**
	 * 自定义shiro 拦截
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnProperty(name = "shiro.filter-url", havingValue = "")
	public FilterRegistrationBean filterRegistrationBean(ShiroProperties shiroProperties) {
		FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
		filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
		// 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
		filterRegistration.addInitParameter("targetFilterLifecycle", "true");
		filterRegistration.setEnabled(true);
		filterRegistration.addUrlPatterns(shiroProperties.getFilterUrl());// 可以自己灵活的定义很多，避免一些根本不需要被Shiro处理的请求被包含进来
		return filterRegistration;
	}

	@Bean(name = "lifecycleBeanPostProcessor")
	public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	/**
	 * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全
	 * 逻辑验证 *
	 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
	 * * @return
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnProperty(name = "shiro.open-permissions-annotation", havingValue = "true")
	@DependsOn({ "lifecycleBeanPostProcessor" })
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		advisorAutoProxyCreator.setProxyTargetClass(true);
		return advisorAutoProxyCreator;
	}

	/**
	 * shiro注解支持
	 * 
	 * @param securityManager
	 * @return
	 */
	@Bean
	@ConditionalOnProperty(name = "shiro.open-permissions-annotation", havingValue = "true")
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

}
